#include "predictalgorithm.h"
#include "AlgorithmShare.h"
#include "../mainframe/mainshare.h"

#include <QFile>
#include <QTextStream>
#include <QDataStream>

#define SVM_CLASSIFIER "svm_classify.exe"
#define SVM_MODEL_FILE "etc/model_SVM"
#define SVM_MAXMIN_FILE "etc/829_592MaxMin_SVM.txt"
#define SVM_PREPROCESS_FILE "etc/preprocess_SVM.txt"
#define SVM_RESULT_FILE "etc/result_SVM.txt"
#define NSTAGE_CLASSIFIER "nstageClassify.exe"
#define NSTAGE_MODEL_FILE "etc/model_Nstage"
#define NSTAGE_MAXMIN_FILE "etc/697_593MaxMin_Nstage.txt"
#define NSTAGE_PREPROCESS_FILE "etc/preprocess_Nstage.txt"
#define NSTAGE_RESULT_FILE "etc/result_Nstage.txt"

PredictAlgorithm::PredictAlgorithm(PredictParam *param, QObject *parent):
    m_param(param),
    svmOk(false),
    nstageOk(false),
    AbstractAlgorithm(parent)
{
}

void PredictAlgorithm::run()
{
    if (!m_param) {
        emit resultReady(false, tr("Illegal Parameters"));
        return;
    }

    svmOk = false;
    nstageOk = false;

    prepareSvmParam();
    QProcess *p_svm = new QProcess;
    connect(p_svm, SIGNAL(finished(int)), this, SLOT(on_Svm_Finished(int)));
    connect(p_svm, SIGNAL(finished(int)), p_svm, SLOT(deleteLater()));
    p_svm->start(SVM_CLASSIFIER, QStringList() << SVM_PREPROCESS_FILE << SVM_MODEL_FILE << SVM_RESULT_FILE);
    if (!p_svm->waitForStarted()) {
        emit resultReady(false, tr("Svm classifier start failed"));
        return;
    }

    prepareNstageParam();
    QProcess *p_nstage = new QProcess;
    connect(p_nstage, SIGNAL(finished(int)), this, SLOT(on_Nstage_Finished(int)));
    connect(p_nstage, SIGNAL(finished(int)), p_nstage, SLOT(deleteLater()));
    p_nstage->start(NSTAGE_CLASSIFIER, QStringList() << NSTAGE_PREPROCESS_FILE << NSTAGE_MODEL_FILE << NSTAGE_RESULT_FILE);
    if (!p_nstage->waitForStarted()) {
        emit resultReady(false, tr("Nstage classifier start failed"));
        return;
    }

    exec();
}

void PredictAlgorithm::prepareSvmParam()
{
    //int mrmr[] = { 512, 243, 109, 136, 225, 351, 10, 572, 514, 235, 589, 296, 434, 258, 528, 221, 116, 347, 497, 230, 510, 3, 568, 355, 588 };
    int mrmr[] = {482, 545, 242, 109, 221, 351, 458, 136, 106, 560};
    const char *memos[] = {
        "Feature482:GWFeature[0][5].GPTRentropy",
        "Feature545:GWFeature[0][26].GPTRentropy",
        "Feature242:FOFeature.[2].RootMeanSquare",
        "Feature109:RLFeature[0].LongRunLowGrayLevelEmphasis",
        "Feature221:RLFeature[1].HighGrayLevelRunEmphsis",
        "Feature351:FOFeature.[3].Kurtosis",
        "Feature458:RLFeature[3].LowGrayLvelRunEmphasis",
        "Feature136:COFeature[1,0].Correlation",
        "Feature106:RLFeature[0].IntensityMean",
        "Feature560: GWFeature[0][31].GPTRentropy"
    };

    // ***********************************************
    QFile data;
    data.setFileName(PROGNOSTIC_FEATURE_FILE);
    data.open(QIODevice::ReadOnly);
    QDataStream din(&data);
    double *m_OrgFeatures = new double[592];
    char *d = new char[8];
    for (int i = 0; i<592; i++){
        //din >> m_OrgFeatures[i];
        din.readRawData(d, 8);
        m_OrgFeatures[i] = *((double*)d);
    }
    delete d;
    data.close();

    data.setFileName(MAIN_FEATURE_FILE);
    data.open(QIODevice::WriteOnly);
    QTextStream feaOut(&data);
    int count = sizeof(mrmr)/sizeof(mrmr[0]);
    for (int i = 0; i < count; ++i) {
        feaOut << "#" << memos[i] << "\n";
        feaOut << m_OrgFeatures[mrmr[i]-1] << "\n\n";
    }
    data.close();

    /*
    data.setFileName(PROGNOSTIC_FEATURE_FILE".check");
    data.open(QIODevice::WriteOnly);
    QTextStream feaOut(&data);
    for (int i = 0; i < 592; ++i)
        feaOut << m_OrgFeatures[i] << " ";
    data.close();
    */
    // **********************************************
/*
    // ----------------------------------------------
    data.setFileName("etc/featureFile.data");
    data.open(QIODevice::ReadOnly);
    din.setDevice(&data);
    d = new char[8];
    for (int i = 0; i<592; i++){
        //din >> m_OrgFeatures[i];
        din.readRawData(d, 8);
        m_OrgFeatures[i] = *((double*)d);
    }
    delete d;
    data.close();


    data.setFileName("etc/featureFile.data.check");
    data.open(QIODevice::WriteOnly);
    feaOut.setDevice(&data);
    for (int i = 0; i < 592; ++i)
        feaOut << m_OrgFeatures[i] << " ";
    data.close();
    // --------------------------------------------------
*/

    double *m_MaxMin[2];
    m_MaxMin[0] = new double[592];
    m_MaxMin[1] = new double[592];
    data.setFileName(SVM_MAXMIN_FILE);
    data.open(QIODevice::ReadOnly);
    QTextStream tin(&data);
    tin.setDevice(&data);
    for (int i = 0; i<592; i++){
        tin >> m_MaxMin[0][i];
        tin >> m_MaxMin[1][i];
    }
    data.close();

    /*
    QFile mm(SVM_MAXMIN_FILE".check");
    mm.open(QIODevice::WriteOnly);
    QTextStream mmOut(&mm);
    for (int i = 0; i < 592; ++i)
        mmOut << m_MaxMin[0][i] << " " << m_MaxMin[1][i] << " ";
    */

    data.setFileName(SVM_PREPROCESS_FILE);
    data.open(QIODevice::WriteOnly);
    QTextStream tout(&data);
    tout << 0 << " ";
    int MRMRlength = (sizeof(mrmr) / sizeof(mrmr[0]));
    int *m_MRMRNum = new int[MRMRlength];
    for (int i = 0; i<MRMRlength; i++){
        m_MRMRNum[i] = mrmr[i] - 1;
        int j = i + 1;
        double nor_feature = -1;
        if ((m_MaxMin[0][m_MRMRNum[i]] - m_MaxMin[1][m_MRMRNum[i]]) != 0){
            nor_feature = 2 * (m_OrgFeatures[m_MRMRNum[i]] - m_MaxMin[1][m_MRMRNum[i]]) / (m_MaxMin[0][m_MRMRNum[i]] - m_MaxMin[1][m_MRMRNum[i]]) - 1;
        }
        tout << j << ":" << qSetRealNumberPrecision(6) << fixed << nor_feature << " ";
    }
    tout << '\n';
    data.close();

    delete[] m_OrgFeatures;
    delete[] m_MaxMin[0];
    delete[] m_MaxMin[1];
    delete[] m_MRMRNum;
}

void PredictAlgorithm::on_Svm_Finished(int code)
{
    QFile svm(SVM_RESULT_FILE);
    if (svm.open(QIODevice::ReadOnly)) {
        QTextStream in(&svm);
        in >> m_param->r_svm;
        svmOk = true;
        if (nstageOk) {
            emit resultReady(true, tr("Read result Ok"));
            quit();
        }
        svm.close();
    } else {
        emit resultReady(false, tr("Read svm result failed"));
        quit();
    }
}

void PredictAlgorithm::prepareNstageParam()
{
    int mrmr[]={135,478,534,390,348,359,123,225,589,569};

    QFile data;
    data.setFileName(PROGNOSTIC_FEATURE_FILE);
    data.open(QIODevice::ReadOnly);
    QDataStream din(&data);
    double *m_OrgFeatures = new double[593];
    char *d = new char[8];
    for (int i = 0; i<592; i++){
        //din >> m_OrgFeatures[i];
        din.readRawData(d, 8);
        m_OrgFeatures[i] = *((double*)d);
    }
    delete d;
    data.close();

    double *m_MaxMin[2];
    m_MaxMin[0] = new double[593];
    m_MaxMin[1] = new double[593];
    data.setFileName(NSTAGE_MAXMIN_FILE);
    data.open(QIODevice::ReadOnly);
    QTextStream tin(&data);
    tin.setDevice(&data);
    for (int i = 0; i<593; i++){
        tin >> m_MaxMin[0][i];
        tin >> m_MaxMin[1][i];
    }
    data.close();

    data.setFileName(NSTAGE_PREPROCESS_FILE);
    data.open(QIODevice::WriteOnly);
    QTextStream tout(&data);
    tout << 0 << " ";
    int MRMRlength = (sizeof(mrmr) / sizeof(mrmr[0]));
    int *m_MRMRNum = new int[MRMRlength];
    for (int i = 0; i<MRMRlength; i++){
        m_MRMRNum[i] = mrmr[i] - 2;
        int j = i + 1;
        double nor_feature = -1;
        if ((m_MaxMin[0][m_MRMRNum[i]] - m_MaxMin[1][m_MRMRNum[i]]) != 0){
            nor_feature = 2 * (m_OrgFeatures[m_MRMRNum[i]] - m_MaxMin[1][m_MRMRNum[i]]) / (m_MaxMin[0][m_MRMRNum[i]] - m_MaxMin[1][m_MRMRNum[i]]) - 1;
        }
        tout << j << ":" << qSetRealNumberPrecision(6) << fixed << nor_feature << " ";
    }
    tout << '\n';
    data.close();

    delete[] m_OrgFeatures;
    delete[] m_MaxMin[0];
    delete[] m_MaxMin[1];
    delete[] m_MRMRNum;
}

void PredictAlgorithm::on_Nstage_Finished(int code)
{
        QFile nstage(NSTAGE_RESULT_FILE);
        if (nstage.open(QIODevice::ReadOnly)) {
            QTextStream in(&nstage);
            in >> m_param->r_nstage;
            nstageOk = true;
            if (svmOk) {
                emit resultReady(true, tr("Read result Ok"));
                quit();
            }
            nstage.close();
        } else {
            emit resultReady(false, tr("Read nstage result file failed"));
            quit();
        }
}
